﻿// This file is a script that can be executed with the F# Interactive.  
// It can be used to explore and test the library project.
// Note that script files will not be part of the project build.

#r "../Debug/Cephei.Kernel.dll"
#r "../Debug/Cephei.Core.dll"
#r "../Debug/Cephei.QL121.dll"
//#r "../Debug/Cephei.QL101.impl.dll"
#r "../Debug/Cephei.Fun121.dll"


open System
open Cephei

//
// Create a Yield Curve object with domain market data
//
let YieldCurve months quotes referenceDate = 
    let calendar =  Fun.Times.Calendars.TARGET.Create ()
    let depositVector = 
        let depositRates =
            let quote v = Fun.Quotes.SimpleQuote.Create (Some v)
            let three_months = Fun.Times.Period.Create (3, QL.Times.TimeUnitEnum.Months)
            let day_counter = Fun.Times.Daycounters.Actual365Fixed.Create ()
            let modfollow = QL.Times.BusinessDayConventionEnum.ModifiedFollowing
            let periods =
                let period p =
                    Fun.Times.Period.Create (p, QL.Times.TimeUnitEnum.Months)
                Array.map (period) months
            let helper v m = 
                let fixingDays = 3u
                Fun.Termstructures.Yield.DepositRateHelper.Create 
                    ( quote(v)
                    , m
                    , fixingDays
                    , calendar
                    , modfollow
                    , true
                    , day_counter
                    ) :> QL.Termstructures.Yield.IRateHelper 
            Array.map2 (helper) quotes periods
        Fun.Termstructures.Yield.RateHelper.CreateVector (depositRates)
    let ActActISDA = 
        Fun.Times.Daycounters.ActualActual.Create (Some QL.Times.Daycounters.ActualActual.ConventionEnum.ISDA) 
    let tolerance = 1.0e-15  
    let refDate = calendar.Adjust (referenceDate, Some QL.Times.BusinessDayConventionEnum.ModifiedFollowing)
    Fun.Termstructures.Yield.PiecewiseYieldCurveDiscountLogLinear.Create 
        ( refDate
        , depositVector
        , ActActISDA
        , tolerance
        ) :> QL.Termstructures.IYieldTermStructure

//
// Define a Zero Coupon Bond
//
let ZeroCouponBond curve maturityDate issueDate faceAmount redeption =
    let pricer = 
        Fun.Pricingengines.Bond.DiscountingBondEngine.Create (Some (curve), None)
    let usCalendar = Fun.Times.Calendars.UnitedStates.Create (Some QL.Times.Calendars.UnitedStates.MarketEnum.GovernmentBond)
    let matDate = usCalendar.Adjust (maturityDate, Some QL.Times.BusinessDayConventionEnum.ModifiedFollowing )
    let isDate = usCalendar.Adjust (issueDate, Some QL.Times.BusinessDayConventionEnum.ModifiedFollowing )
    let paymentConvention = Some QL.Times.BusinessDayConventionEnum.Following
    let settlementDays = 3u
    Fun.Instruments.Bonds.ZeroCouponBond.Create 
        ( settlementDays
        , usCalendar
        , faceAmount
        , matDate
        , paymentConvention
        , Some redeption
        , Some isDate
        , pricer
        ) :> QL.Instruments.IBond 

//
// Define a Fixed Rate Bond
//
let FixedRateBond curve effectiveDate termDate faceAmount coupons = 
    let settlementDays = 3u
    let usCalendar = Fun.Times.Calendars.UnitedStates.Create (Some QL.Times.Calendars.UnitedStates.MarketEnum.GovernmentBond) :> Cephei.QL.Times.ICalendar
    let pricer = 
        Fun.Pricingengines.Bond.DiscountingBondEngine.Create (Some (curve), None)
    let fixedBondSchedule = 
        let fixedPeriod = Fun.Times.Period.Create (QL.Times.FrequencyEnum.Semiannual)
        let Unajusted = QL.Times.BusinessDayConventionEnum.Unadjusted 
        let backwards = QL.Times.DateGeneration.RuleEnum.Backward 
        Fun.Times.Schedule.Create 
            ( effectiveDate 
            , termDate 
            , fixedPeriod 
            , usCalendar 
            , Unajusted 
            , Unajusted 
            , backwards 
            , false 
            , None 
            , None
            )
    let couponsV = Fun.Doubles.CreateVector (coupons)
    let actualActual = Fun.Times.Daycounters.ActualActual.Create (Some QL.Times.Daycounters.ActualActual.ConventionEnum.Bond)
    let modFollow = QL.Times.BusinessDayConventionEnum.ModifiedFollowing 
    Fun.Instruments.Bonds.FixedRateBond.Create 
        ( settlementDays 
        , faceAmount 
        , fixedBondSchedule 
        , couponsV 
        , actualActual 
        , Some modFollow 
        , Some faceAmount 
        , Some effectiveDate  
        , Some usCalendar
        , pricer  
        ) :> QL.Instruments.IBond 

//
// Report Functions
//
let NPVs (bl : QL.Instruments.IBond list) = 
    List.map (fun (b : QL.Instruments.IBond) -> b.NPV) bl

let CleanPrices (bl : QL.Instruments.IBond list) = 
    List.map (fun (b : QL.Instruments.IBond) -> b.CleanPrice() ) bl

let DirtyPrices (bl : QL.Instruments.IBond list) = 
    List.map (fun (b : QL.Instruments.IBond) -> b.DirtyPrice() ) bl

//
// Sample Data
//

let OurCurve = 
    let months = [| 3; 6; 12; 24; 375 |]
    let quotes = [| 0.0096; 0.0145; 0.0194; 2.0; 2.5 |]
    let refDate = DateTime.Now.AddDays -2.0
    YieldCurve months quotes refDate 

let zbond1 = 
    let curve = OurCurve
    let maturity = DateTime(2013,1,1)
    let issue = DateTime(2011,1,1)
    let face = 100.0
    let mature = 116.0
    ZeroCouponBond curve maturity issue face mature

let fbond1 = 
    let curve = OurCurve
    let term = DateTime(2013,1,1)
    let effect = DateTime(2011,1,1)
    let face = 100.0
    let coupons = [|1.0; 0.045|]
    FixedRateBond curve effect term face coupons

//
// Report
//
let Report = 
    let s = Fun.Sessions.Create (DateTime(2013,1,1))
    let bonds = s.With [fbond1] //[zbond1] // ; fbond1]
    let layout = 
        [ sprintf "    %A\n" [ "Zero"    ; "Fixed" ]
        ; sprintf "NPV %A\n" (NPVs bonds)
        ; sprintf "CP  %A\n" (CleanPrices bonds)
        ; sprintf "DP  %A\n" (DirtyPrices bonds)
        ]
    List.map (printf "%s") layout

let goldenRatio =
    let avg a b = 
        ((a - b) / 2.0) + b
    let rec ratio x = 
        printf "%f\n" x
        let X = (x + 1.0) / x
        match X - x with
        | 0.0 -> x
        | ___ -> ratio (avg X x)
    ratio 42.0

let e = 
    let sensitity = 1e-12
    let rec invfac a b c = 
        let B = b + 1.0
        let C = B * c
        let A = 1.0 / C
        match A < sensitity with
        | true  -> a + A
        | false -> a + invfac A B C
    invfac 1.0 0.0 1.0

let CF a b =
    let min x y = 
        match x < y with
        | true  -> x
        | false -> y
    let factor x y =
        x = (x / y) * y
    let rec findFactor ia ib ix = 
         match factor ia ix && factor ib ix || ix = 1 with
         | true -> ix
         | false -> findFactor ia ib (ix - 1)
    findFactor a b (min a b)

let simp a b = 
    let x = CF a b
    [a / x; b / x]

[8 .. 31]
|> List.map (fun (x:int) -> System.Convert.ToString(x, 2))
|> List.filter (fun (s:string) -> not (s.Contains ("00")))
|> (fun l -> l.Length)

let factors v listFactor =
    List.filter (fun n -> v % n = 0) listFactor

let primeslist upto = 
    let newFactor listFactor v = 
        match (factors v listFactor).Length = 0 with
        | true -> listFactor @ [v]
        | false -> listFactor
    List.fold newFactor [] [2 .. upto] 

let primes1 upto = 
    List.fold (fun l v -> if (List.filter (fun n -> v % n = 0) l).Length = 0 then l @ [v] else l) [] [2.. upto]

let primes upto = 
    List.fold (fun l v -> if (List.filter (fun n -> v % n = 0) l).Length = 0 then [v] @ l else l) [2] [for i in 1 .. upto/2 -> i * 2 + 1] 

let primess upto = 
    Seq.fold (fun l v -> if (List.filter (fun n -> v % n = 0) l).Length = 0 then [v] @ l else l) [2] (seq {for i in 1 .. upto/2 do yield (i * 2 + 1)})

let primes2 upto = 
    List.fold (fun l v -> if (List.filter (fun n -> v % n = 0) l).Length = 0 then l @ [v] else l) [2] [for i in 1 .. upto/2 -> i * 2 + 1] 

let primes6 = primes (int 1e6)

let primeFactors v = List.filter (fun n -> v % n = 0) primes6

type StopWatch () =
    let timer = new System.Diagnostics.Stopwatch ()
    do timer.Start() 
    interface System.IDisposable with
        member this.Dispose () = printf "Runtime : %d ms\n" timer.ElapsedMilliseconds 

let timer f x = 
    use s = new StopWatch()
    f x

(*
let timer f x =
    let timer = new System.Diagnostics.Stopwatch ()
    timer.Start ()
    try f x finally
    printf "Runtime : %d ms\n" timer.ElapsedMilliseconds 
    *)
timer primes 100

timer primes (int 1e6)
timer primess (int 1e6)
timer primes (int 1e6)
timer primes2 (int 1e6)
